﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace zijian666.AnyExtensions;

/// <summary>
/// 用于动态构建单一参数的表达式树
/// </summary>
/// <typeparam name="TSource"></typeparam>
public struct LinqExpressionBuilder<TSource>
{
    private ParameterExpression _parameter;

    /// <summary>
    /// 表达式树参数
    /// </summary>
    public ParameterExpression Parameter => _parameter ??= Expression.Parameter(typeof(TSource));

    public Expression<Func<TSource, bool>> Equal(string columnName, object value)
        => Parameter.Property(columnName).Equal(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> Equal(LambdaExpression selector, object value)
        => selector.Body.Proxy(Parameter).Equal(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotEqual(string columnName, object value)
        => Parameter.Property(columnName).NotEqual(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotEqual(LambdaExpression selector, object value)
        => selector.Body.Proxy(Parameter).NotEqual(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> GreaterThan(string columnName, IComparable value)
        => Parameter.Property(columnName).GreaterThan(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> GreaterThan(LambdaExpression selector, IComparable value)
        => selector.Body.Proxy(Parameter).GreaterThan(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> GreaterThanOrEqual(string columnName, IComparable value)
        => Parameter.Property(columnName).GreaterThanOrEqual(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> GreaterThanOrEqual(LambdaExpression selector, IComparable value)
        => selector.Body.Proxy(Parameter).GreaterThanOrEqual(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> LessThan(string columnName, IComparable value)
        => Parameter.Property(columnName).LessThan(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> LessThan(LambdaExpression selector, IComparable value)
        => selector.Body.Proxy(Parameter).LessThan(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> LessThanOrEqual(string columnName, IComparable value)
        => Parameter.Property(columnName).LessThanOrEqual(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> LessThanOrEqual(LambdaExpression selector, IComparable value)
        => selector.Body.Proxy(Parameter).LessThanOrEqual(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> Between(string columnName, IComparable? minimum, IComparable? maximum)
        => Parameter.Property(columnName).Between(minimum, maximum).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> Between(LambdaExpression selector, IComparable? minimum, IComparable? maximum)
        => selector.Body.Proxy(Parameter).Between(minimum, maximum).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotBetween(string columnName, IComparable minimum, IComparable maximum)
        => Parameter.Property(columnName).NotBetween(minimum, maximum).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotBetween(LambdaExpression selector, IComparable minimum, IComparable maximum)
        => selector.Body.Proxy(Parameter).NotBetween(minimum, maximum).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> Like(string columnName, string value)
        => Parameter.Property(columnName).Like(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> Like(string columnName, IEnumerable<string> value)
        => Parameter.Property(columnName).Like(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> Like(string columnName, IQueryable<string> value)
        => Parameter.Property(columnName).Like(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> Like(LambdaExpression selector, string value)
        => selector.Body.Proxy(Parameter).Like(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> Like(LambdaExpression selector, IEnumerable<string> value)
        => selector.Body.Proxy(Parameter).Like(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> Like(LambdaExpression selector, IQueryable<string> value)
        => selector.Body.Proxy(Parameter).Like(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotLike(string columnName, string value)
        => Parameter.Property(columnName).NotLike(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotLike(string columnName, IEnumerable<string> value)
        => Parameter.Property(columnName).NotLike(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotLike(string columnName, IQueryable<string> value)
        => Parameter.Property(columnName).NotLike(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotLike(LambdaExpression selector, string value)
        => selector.Body.Proxy(Parameter).NotLike(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotLike(LambdaExpression selector, IEnumerable<string> value)
        => selector.Body.Proxy(Parameter).NotLike(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotLike(LambdaExpression selector, IQueryable<string> value)
        => selector.Body.Proxy(Parameter).NotLike(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> StartsWith(string columnName, string value)
        => Parameter.Property(columnName).StartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> StartsWith(string columnName, IEnumerable<string> value)
        => Parameter.Property(columnName).StartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> StartsWith(string columnName, IQueryable<string> value)
        => Parameter.Property(columnName).StartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> StartsWith(LambdaExpression selector, string value)
        => selector.Body.Proxy(Parameter).StartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> StartsWith(LambdaExpression selector, IEnumerable<string> value)
        => selector.Body.Proxy(Parameter).StartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> StartsWith(LambdaExpression selector, IQueryable<string> value)
        => selector.Body.Proxy(Parameter).StartsWith(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotStartsWith(string columnName, string value)
        => Parameter.Property(columnName).NotStartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotStartsWith(string columnName, IEnumerable<string> value)
        => Parameter.Property(columnName).NotStartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotStartsWith(string columnName, IQueryable<string> value)
        => Parameter.Property(columnName).NotStartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotStartsWith(LambdaExpression selector, string value)
        => selector.Body.Proxy(Parameter).NotStartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotStartsWith(LambdaExpression selector, IEnumerable<string> value)
        => selector.Body.Proxy(Parameter).NotStartsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotStartsWith(LambdaExpression selector, IQueryable<string> value)
        => selector.Body.Proxy(Parameter).NotStartsWith(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> EndsWith(string columnName, string value)
        => Parameter.Property(columnName).EndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> EndsWith(string columnName, IEnumerable<string> value)
        => Parameter.Property(columnName).EndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> EndsWith(string columnName, IQueryable<string> value)
        => Parameter.Property(columnName).EndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> EndsWith(LambdaExpression selector, string value)
        => selector.Body.Proxy(Parameter).EndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> EndsWith(LambdaExpression selector, IEnumerable<string> value)
        => selector.Body.Proxy(Parameter).EndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> EndsWith(LambdaExpression selector, IQueryable<string> value)
        => selector.Body.Proxy(Parameter).EndsWith(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> NotEndsWith(string columnName, string value)
        => Parameter.Property(columnName).NotEndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotEndsWith(string columnName, IEnumerable<string> value)
        => Parameter.Property(columnName).NotEndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotEndsWith(string columnName, IQueryable<string> value)
        => Parameter.Property(columnName).NotEndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotEndsWith(LambdaExpression selector, string value)
        => selector.Body.Proxy(Parameter).NotEndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotEndsWith(LambdaExpression selector, IEnumerable<string> value)
        => selector.Body.Proxy(Parameter).NotEndsWith(value).Lambda<Func<TSource, bool>>(Parameter);
    public Expression<Func<TSource, bool>> NotEndsWith(LambdaExpression selector, IQueryable<string> value)
        => selector.Body.Proxy(Parameter).NotEndsWith(value).Lambda<Func<TSource, bool>>(Parameter);

    public Expression<Func<TSource, bool>> And(Expression left, Expression right)
    {
        if (left is LambdaExpression l1)
        {
            left = l1.Body;
        }
        if (right is LambdaExpression l2)
        {
            right = l2.Body;
        }
        return Expression.AndAlso(left.Proxy(Parameter), right.Proxy(Parameter)).Lambda<Func<TSource, bool>>(Parameter);
    }

    public Expression<Func<TSource, bool>> Or(Expression left, Expression right)
    {
        if (left is LambdaExpression l1)
        {
            left = l1.Body;
        }
        if (right is LambdaExpression l2)
        {
            right = l2.Body;
        }
        return Expression.OrElse(left.Proxy(Parameter), right.Proxy(Parameter)).Lambda<Func<TSource, bool>>(Parameter);
    }

    public Expression<Func<TSource, bool>> Not(Expression expr) => Expression.Not(expr.Proxy(Parameter)).Lambda<Func<TSource, bool>>(Parameter);

    /// <summary>
    /// 获取代表返回特定属性的表达式
    /// </summary>
    public Expression<Func<TSource, TKey>> Property<TKey>(string columnName)
    {
        Expression property = Parameter.Property(columnName);
        if (property.Type != typeof(TKey))
        {
            property = Expression.Convert(property, typeof(TKey));
        }
        return property.Lambda<Func<TSource, TKey>>(Parameter);
    }

    /// <summary>
    /// 获取代表返回特定属性的表达式
    /// </summary>
    public LambdaExpression Property(string columnName)
    {
        var property = Parameter.Property(columnName);
        return Expression.Lambda(property, Parameter);
    }
}